//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <mantle.h>
#include <testing/timepeg.h>
#include "tp_print.h"

struct timepeg_slot *g_timepeg_slots = 0;
struct timepeg_slot *g_prev_slots = 0;

Thread *g_pPrevThread = 0;

void timepeg_asm()
{
    static struct timepeg_slot _x_Timepeg = {\
            "InterruptHandler", \
    };
    timepeg_hit(&_x_Timepeg, TPH_MODE_START, 1);
}

void tp_schedule_start()
{
    g_pPrevThread = GetCurrentThread();
    TIMEPEG_MODE("ScheduleStart", TPH_MODE_START, 1);
}

void tp_schedule_stop()
{
    if (g_pPrevThread == GetCurrentThread()) {
        DTIMEPEG("ScheduleSameThread", "ScheduleStart");
    }
    else if ((NULL == GetCurrentThread()->m_pOwner)
            || (g_pPrevThread->m_pOwner == GetCurrentThread()->m_pOwner)) {
        DTIMEPEG("ScheduleSameProcess", "ScheduleStart");
    }
    else {
        DTIMEPEG("ScheduleDiffProcess", "ScheduleStart");
    }

    struct timepeg_slot * temp = g_timepeg_slots;
    static bool bPrint = false;
    if (!bPrint) {
        while (temp) {
            int j = TST_TIMES - 1;
            if (0 == temp->value[j]) {
                break;
            }
            temp = temp->next;
            if (!temp->directed_pred) {
                TESTENTRY_KERNEL_EXIT
                bPrint = true;
                ((DzEvent*)g_pRebootEvent)->Notify(1);
            }
        }
    }
}

void timepeg_construct(struct timepeg_slot * const _ntps)
{
    //SPIN LOCK
    UInt32 j;
            //struct timepeg_slot_percpu *ntpsp =
            //  &(_ntps->percpu[i]);

    //  in fact, it's no need to define 16 timepeg_arc structures, we only
    //  use 1 timepeg_arc structure now
    for (j = 0; j < TIMEPEG_NR_PREDS; j++) {
        struct timepeg_arc *ntpa = &(_ntps->preds[j]);
        ntpa->max_tp.high = 0;
        ntpa->max_tp.low = 0;
        ntpa->min_tp.high = 0xffffffff;
        ntpa->min_tp.low = 0xffffffff;
        ntpa->smax_tp.high = 0;
        ntpa->smax_tp.low = 0;
        ntpa->smin_tp.high = 0xffffffff;
        ntpa->smin_tp.low = 0xffffffff;
    }

    // the following is only for the "STOP" to find it's corresponding node
    if (!(_ntps->mode_bits & TIMEPEG_MODE_DEAD) && _ntps->directee_name) {
        struct timepeg_slot *tps = g_timepeg_slots;
        while (tps) {
            if (!strcmp(tps->name, _ntps->directee_name))
                break;
            tps = tps->next;
        }

        if (0 == tps) {
            _ntps->mode_bits |= TIMEPEG_MODE_DEAD;
        }
        else {
            _ntps->directed_pred = tps;
        }
    }

    if (!(_ntps->mode_bits & TIMEPEG_MODE_DEAD)) {
        _ntps->next = g_timepeg_slots;
        g_timepeg_slots = _ntps;
    }

    return;
}

TIMESTAMP * timepeg_do_hit(struct timepeg_slot * const _ntps, \
    TIMESTAMP * const _now_tp, const int _mode)
{
    struct timepeg_slot * prev_tp;      /* Most-recently-visited*/

    TIMESTAMP *retval = (TIMESTAMP *)0;

    if (_ntps->mode_bits & TIMEPEG_MODE_DEAD) {
        goto bye;
    }

    if (0 == (_ntps->mode_bits & TIMEPEG_MODE_INITIALISED)) {
        /* First time this timepeg has been used */
        timepeg_construct(_ntps);
        //if (_ntps->mode_bits & TIMEPEG_MODE_INITIALISED)
        //    ;
        // TIMEPEG_MODE_INITIALISED=(decimal)1
        // TPH_MODE_START=(decimal)1
        // so, make the _ntps->mode_bits=(binary)0000000000000001
        _ntps->mode_bits |= TIMEPEG_MODE_INITIALISED;
    }

    if (_ntps->directed_pred) {
        /* Directed */
        prev_tp = _ntps->directed_pred;
    }
    else {
        /* Most-recently-seen for this CPU */
        prev_tp = g_prev_slots;
    }

    // after first node's initializing, the "prev_tp" should be NULL
    // but from the second time it came, the prev_tp was not NULL
    if (0 == prev_tp) {
        /* DO NOTHING */
    }
    else if (_mode & TPH_MODE_STOP) {
        struct timepeg_arc *ntpa = 0;
        TIMESTAMP elapsed_tp;
        int i;

        ntpa = &(_ntps->preds[0]);
        for (i = 0; i < TIMEPEG_NR_PREDS; i++, ntpa++) {
            if (0 == ntpa->timepeg) {
                /*
                 * End of the list.  This must be
                 * the first time we've seen this arc
                 */
                // the first time it came here is after the first timepeg_slot
                // node initialized, so, the "prev_tp" is pointing to it
                ntpa->timepeg = prev_tp;
                break;
            }
            if (ntpa->timepeg == prev_tp) {
                /* We've used this arc before */
                break;
            }
        }
        if (i == TIMEPEG_NR_PREDS) {
            if (0 == (_ntps->mode_bits & TIMEPEG_MODE_FULL)) {
                //int j;
                struct timepeg_arc *ntpa2;
                // now _ntps->mode_bits=(binary)0000000000000001
                // TIMEPEG_MODE_FULL=(decimal)2=(decimal)10
                // so, make the _ntps->mode_bits=(binary)0000000000000011
                _ntps->mode_bits |= TIMEPEG_MODE_FULL;
                ntpa2 = &(_ntps->preds[0]);
                /*print log*/
            }
        }
        else {
            ntpa->nr_times++;

            elapsed_tp = *_now_tp;
            // in the first time it came here, the "prev_tp->departure_tp" is 0
            // as the result, "elapsed_tp" stored the CPU cycles from the
            // computer's start up
            // in the second time, it use the current time-stamp to sub the
            // time-stamp which had got before and stored in the node
            timepeg_sub(&(prev_tp->departure_tp), &elapsed_tp);

            UInt32 * presult = 0;
            int subscript;
            for (subscript = 0; subscript < TST_TIMES; subscript++) {
                presult = &(_ntps->value[subscript]);

                if (*presult) {
                    continue;
                }

                *presult = elapsed_tp.low;
                break;
            }

            timepeg_add(&elapsed_tp, &(ntpa->acc_tp));

            if (timepeg_cmp(&elapsed_tp, &ntpa->smin_tp) < 0) {
                if (timepeg_cmp(&elapsed_tp, &ntpa->min_tp) < 0) {
                    ntpa->smin_tp = ntpa->min_tp;
                    ntpa->min_tp = elapsed_tp;              //Get minimal value
                }
                else {
                    ntpa->smin_tp = elapsed_tp;
                }
            }

            if (timepeg_cmp(&elapsed_tp, &ntpa->smax_tp) > 0) {
                if (timepeg_cmp(&elapsed_tp, &ntpa->max_tp) > 0) {
                    ntpa->smax_tp = ntpa->max_tp;
                    ntpa->max_tp = elapsed_tp;              //Get maximal value
                }
                else {
                    ntpa->smax_tp = elapsed_tp;
                }
            }
        }
    }

    // we wouldn't get the time stamp for the nodes which had specified as DEAD
    if (_mode & TPH_MODE_START) {
        /*
         * Note the fact that this CPU's next arc starts on this timepeg
         */
        // after the first time's initiazling, "g_prev_slots" would point to
        // the first node from now on, and "g_timepeg_slots" had already
        // pointed to the first node in timepeg_construct();
        g_prev_slots = _ntps;
        /*
         * The outgoing inline code writes the current time into this CPU's
         * slot in this timepeg
         */
        retval = &(_ntps->departure_tp);
    }

bye:
    return retval;
}
